package compiler.mine;

import compiler.*;

import java.util.Set;

/**
 * Created by szj on 2017/2/2.
 */
public class NfaConstructor {
    private NfaManager manager;
    private Lexer lexer;

    public NfaConstructor(Lexer lexer) throws Exception {
        this.manager = new NfaManager();
        this.lexer = lexer;
    }

    /**
     * expr -> expr OR cat_expr
     *       | cat_expr
     */
    public void expr(NfaPair pair) throws Exception {
        catExpr(pair);
        NfaPair anotherPair = new NfaPair();

        while (lexer.match(Lexer.Token.OR)) {
            lexer.advance();
            catExpr(anotherPair);
            Nfa start = manager.newNfa();
            start.next = pair.startNode;
            start.next2 = anotherPair.startNode;
            pair.startNode = start;

            Nfa end = manager.newNfa();
            pair.endNode.next = end;
            anotherPair.endNode.next = end;
            pair.endNode = end;
        }
    }

    /**
     * cat_expr-> factor cat_expr
     *          | factor
     */
    public void catExpr(NfaPair pair) throws Exception {

        if (validateFirstToken(lexer.getCurrentToken())) {
            factor(pair);
        }

        NfaPair anotherPair = new NfaPair();

        while (validateFirstToken(lexer.getCurrentToken())) {
            //如果只有一个factor,那么这里进不来
            factor(anotherPair);
            pair.endNode.next = anotherPair.startNode;
            pair.endNode = anotherPair.endNode;
        }
    }

    /**
     * factory -> term| (expr) | term* | term+ | term?
     */
    public void factor(NfaPair pair) throws Exception {
        term(pair);
        boolean handled = constructExprInParen(pair);
        if (!handled) {
            handled = constructKleeneClosure(pair);
        }
        if (!handled) {
            handled = constructPlusClosure(pair);
        }
        if (!handled) {
            constructOptionalClosure(pair);
        }
    }

    public boolean constructKleeneClosure(NfaPair pair) throws Exception {
        //term*
        if (!lexer.match(Lexer.Token.ASTERISK)) {
            return false;
        }
        Nfa start, end;
        term(pair);

        start = manager.newNfa(); // 边默认是空串
        end = manager.newNfa();
        start.next = pair.startNode;
        start.next2 = end;
        pair.endNode.next = end;
        pair.endNode.next2 = pair.startNode;
        pair.startNode = start;
        pair.endNode = end;

        lexer.advance();
        return true;
    }

    public boolean constructPlusClosure(NfaPair pair) throws Exception {
        if (!lexer.match(Lexer.Token.PLUS)) {
            return false;
        }
        Nfa start, end;
        term(pair);

        start = manager.newNfa();
        end = manager.newNfa();
        start.next = pair.startNode;
        pair.endNode.next = end;
        pair.endNode.next2 = pair.startNode;
        pair.startNode = start;
        pair.endNode = end;

        lexer.advance();
        return true;
    }

    public boolean constructExprInParen(NfaPair pair) throws Exception {
        if (!lexer.match(Lexer.Token.LEFT_PAREN)) {
            return false;
        }

        lexer.advance();
        expr(pair);

        if (lexer.match(Lexer.Token.RIGHT_PAREN)) {
            lexer.advance();
        } else {
            ErrorHandler.parseErr(ErrorHandler.Error.E_BADEXPR);
        }
        return true;
    }

    public boolean constructOptionalClosure(NfaPair pair) throws Exception {
        if (!lexer.match(Lexer.Token.QUESTION_MARK)) {
            return false;
        }
        Nfa start, end;
        term(pair);

        start = manager.newNfa();
        end = manager.newNfa();
        start.next = pair.startNode;
        start.next2 = end;
        pair.endNode.next = end;
        pair.startNode = start;
        pair.endNode = end;

        lexer.advance();
        return true;
    }

    public boolean validateFirstToken(Lexer.Token firstToken) {
        switch (firstToken) {
            case EOS:
            case END:
            case RIGHT_PAREN:
            case RIGHT_BRACKET:
            case RIGHT_BRACE:
            case ASTERISK:
            case PLUS:
            case QUESTION_MARK:
            case OR:
                return false;
        }
        return true;
    }

    /**
     * single char | . | [...] | [^...]
     */
    public void term(NfaPair pair) throws Exception {
        boolean handled;
        handled = constructSingleCharacter(pair);
        if (!handled) {
            handled = constructWithDot(pair);
        }
        if (!handled) {
            constructCharSet(pair);
        }
    }

    public boolean constructSingleCharacter(NfaPair pair) throws Exception {
        if (!lexer.match(Lexer.Token.L)) {
            return false;
        }
        pair.startNode = manager.newNfa();
        pair.endNode = pair.startNode.next = manager.newNfa();
        pair.startNode.setEdge(lexer.getCurrentChar());
        lexer.advance();
        return true;
    }

    public boolean constructWithDot(NfaPair pair) throws Exception {
        if (!lexer.match(Lexer.Token.ANY)) {
            return false;
        }
        pair.startNode = manager.newNfa();
        pair.endNode = pair.startNode.next = manager.newNfa();
        pair.startNode.setEdge(Nfa.CCL);
        pair.startNode.addToSet((byte) '\n');
        pair.startNode.addToSet((byte) '\n');
        pair.startNode.setComplement();
        lexer.advance();
        return true;
    }

    //有取反的 [^...]
    public boolean constructCharSet(NfaPair pair) throws Exception {
        if (!lexer.match(Lexer.Token.LEFT_BRACKET)) {
            return false;
        }

        lexer.advance();
        boolean hasBeginSymbol = false;
        if (lexer.match(Lexer.Token.BEGIN)) {
            //[^...]
            hasBeginSymbol = true;
        }
        pair.startNode = manager.newNfa();
        pair.endNode = pair.startNode.next = manager.newNfa();
        pair.startNode.setEdge(Nfa.CCL);

        if (!lexer.match(Lexer.Token.RIGHT_BRACKET)) {
            handleMinus(pair.startNode.inputSet);
        }
        if (!lexer.match(Lexer.Token.RIGHT_BRACKET)) {
            ErrorHandler.parseErr(ErrorHandler.Error.E_BADEXPR);
        }
        if (hasBeginSymbol) {
            pair.startNode.setComplement();
        }
        lexer.advance();
        return true;
    }

    private void handleMinus(Set<Byte> set) {
        byte left = 0;
        while (!lexer.match(Lexer.Token.EOS)
                && !lexer.match(Lexer.Token.RIGHT_BRACKET)) {
            if (!lexer.match(Lexer.Token.MINUS)) {
                left = (byte) lexer.getCurrentChar();
                set.add(left);
            } else {
                lexer.advance();
                for (; left <= lexer.getCurrentChar(); left++) {
                    set.add(left);
                }
            }
            lexer.advance();
        }
    }
}
